﻿/*
 *	Copyright (c) 2009 Queensland University of Technology. All rights reserved.
 *	The QUT Bioinformatics Collection is open source software released under the 
 *	Microsoft Public License (Ms-PL): http://www.microsoft.com/opensource/licenses.mspx.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml.Linq;
using System.Threading;
using System.Windows.Threading;

namespace TaxToXml {
	/// <summary>
	/// TaxToXml is a utility program that extracts a hierarchical summary 
    /// from a dump of the NCBI taxonomy database and writes it into a gzipped
    /// xml document that can be consumed by BlastExplorer.
	/// </summary>
	public partial class Window1 : Window {
        // TODO: make this emit a gzipped xml document rather than a xml document.
        // TODO: consider moving TaxTpXml into blast explorer, or alternatively making it more suitable for use by humans.

		class TaxRecord {
			public string ScientificName;
			public string GenbankCommonName;
			public decimal TaxId;
			public decimal ParentId;
			public readonly List<TaxRecord> Children = new List<TaxRecord>();
			public bool Saved;
		}

		public Window1 () {
			InitializeComponent();
		}

		Dictionary<decimal, TaxRecord> taxa = new Dictionary<decimal, TaxRecord>();

		private void Load ( object sender, RoutedEventArgs e ) {
			System.Windows.Forms.OpenFileDialog dialog = new System.Windows.Forms.OpenFileDialog();
			dialog.Filter = "*.dmp|*.dmp|All files|*.*";

			if ( dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK ) {
				// MessageBox.Show( "Selected file: " + dialog.FileName );
				FileInfo fileInfo = new FileInfo( dialog.FileName );
				DirectoryInfo containingFolder = fileInfo.Directory;

				Thread thread = new Thread( new ThreadStart( delegate() {
					Load( containingFolder );
				} ) );

				thread.Start();
			}
		}

		private void Load ( DirectoryInfo containingFolder ) {

			#region Read the node definitions.
			{
				StreamReader input = new StreamReader( String.Format( "{0}\\nodes.dmp", containingFolder.FullName ) );
				string [] record = null;
				int i = 0;

				Action showRecords = delegate() {
					results.Text = string.Format( "{0} records loaded", taxa.Count );
				};

				while ( ( record = ReadLine( input ) ) != null ) {
					decimal taxId = Parse( record[0] );
					decimal parentId = Parse( record[1] );

					TaxRecord taxon =  new TaxRecord {
						TaxId = taxId,
						ParentId = parentId,
						ScientificName = "",
						GenbankCommonName = ""
					};
					taxa[taxId] = taxon;

					if ( i++ % 1000 == 0 ) {
						Dispatcher.BeginInvoke( showRecords );
					}
				}

				Dispatcher.BeginInvoke( showRecords );
			}
			#endregion

			#region Attach names to nodes.
			{
				StreamReader input = new StreamReader( String.Format( "{0}\\names.dmp", containingFolder.FullName ) );
				string [] record = null;

				while ( ( record = ReadLine( input ) ) != null ) {
					decimal taxId = Parse( record[0] );
					string name = record[1];
					string nameType = record[3];

					if ( nameType == "scientific name" ) {
						taxa[taxId].ScientificName = name;
					}
					else if ( nameType == "genbank common name" ) {
						taxa[taxId].GenbankCommonName = name;
					}
				}

				Action show = delegate() {
					results.Text += "\nNames have been attached to taxa";
				};
				Dispatcher.BeginInvoke( show );
			}
			#endregion

			#region Make a list of all children for each node.
			{
				foreach ( TaxRecord taxon in taxa.Values ) {
					try {
						taxa[taxon.ParentId].Children.Add( taxon );
					}
					catch {
					}
				}

				Action show = delegate() {
					results.Text += "\nChildren have been identified for taxa.\nReady to save.";
				};
				Dispatcher.BeginInvoke( show );
			}
			#endregion
		}

		public decimal Parse ( string s ) {
			decimal result;

			if ( !decimal.TryParse( s, out result ) ) {
				result = 0;
			}

			return result;
		}

		public string[] ReadLine ( StreamReader input ) {
			string s = input.ReadLine();

			if ( s == null ) {
				return null;
			}

			return s.Replace( "\t|", "" ).Split( '\t' );
		}

		private void Save ( object sender, RoutedEventArgs e ) {
			XDocument doc = new XDocument();
			doc.Add( SaveRecursive( taxa[1] ) );

			System.Windows.Forms.SaveFileDialog dialog = new System.Windows.Forms.SaveFileDialog();
			dialog.Filter = "Xml documents (*.xml)|*.xml|All files|*.*";
			dialog.Title = "Save taxonomic tree";
			if ( dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK ) {
				(new Thread( new ThreadStart( new Action( delegate() {
					StreamWriter output = new StreamWriter( dialog.OpenFile() );
					doc.Save( output );
					Dispatcher.BeginInvoke( new Action( delegate() {
						results.Text += "\nYour document has been saved.";
					} ) );
				} ) ) )).Start();
			}
		}

		private XElement SaveRecursive ( TaxRecord taxon ) {
			XElement node = new XElement( "X",
				new XAttribute( "I", taxon.TaxId ),
				new XAttribute( "S", taxon.ScientificName ),
				new XAttribute( "G", taxon.GenbankCommonName )
			);
			
			taxon.Saved = true;
			
			foreach ( TaxRecord child in taxon.Children ) {
				if ( ! child.Saved ) {
					node.Add( SaveRecursive( child ) );
				}
			}

			return node;
		}

		private void Quit ( object sender, RoutedEventArgs e ) {
			Close();
		}
	}
}
